// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.

package nachos.machine;

import nachos.security.*;
import nachos.threads.*;

import java.io.File;
import java.io.RandomAccessFile;
import java.io.IOException;

/**
 * This class implements a file system that redirects all requests to the host
 * operating system's file system.
 */
public class StubFileSystem implements FileSystem {
    /**
     * Allocate a new stub file system.
     *
     * @param privilege encapsulates privileged access to the Nachos
     *                  machine.
     * @param directory the root directory of the stub file system.
     */
    public StubFileSystem(Privilege privilege, File directory) {
        this.privilege = privilege;
        this.directory = directory;
    }

    public OpenFile open(String name, boolean truncate) {
        if (!checkName(name))
            return null;

        delay();

        try {
            return new StubOpenFile(name, truncate);
        } catch (IOException e) {
            return null;
        }
    }

    public boolean remove(String name) {
        if (!checkName(name))
            return false;

        delay();

        FileRemover fr = new FileRemover(new File(directory, name));
        privilege.doPrivileged(fr);
        return fr.successful;
    }

    private static class FileRemover implements Runnable {
        public FileRemover(File f) {
            this.f = f;
        }

        public void run() {
            successful = f.delete();
        }

        public boolean successful = false;
        private final File f;
    }

    // 模拟 IO 时间
    private void delay() {
        long time = Machine.timer().getTime();
        int amount = 1000;
        ThreadedKernel.alarm.waitUntil(amount);
        Lib.assertTrue(Machine.timer().getTime() >= time + amount);
    }

    private class StubOpenFile extends OpenFileWithPosition {
        StubOpenFile(final String name, final boolean truncate)
                throws IOException {
            super(StubFileSystem.this, name);

            final File f = new File(directory, name);

            if (openCount == maxOpenFiles)
                throw new IOException();

            privilege.doPrivileged(new Runnable() {
                public void run() {
                    getRandomAccessFile(f, truncate);
                }
            });

            if (file == null)
                throw new IOException();

            open = true;
            openCount++;
        }

        // todo 原先的代码有问题
//        private void getRandomAccessFile(File f, boolean truncate) {
//            try {
//                if (!truncate && !f.exists())
//                    return;
//
//                file = new RandomAccessFile(f, "rw");
//
//                if (truncate)
//                    file.setLength(0);
//            } catch (IOException ignored) {
//            }
//        }

        private void getRandomAccessFile(File f, boolean truncate) {
            try {
                if(f.exists()) {
                    file = new RandomAccessFile(f, "rw");
                } else {
                    if(truncate) {
                        file = new RandomAccessFile(f, "rw");
                        file.setLength(0);
                    }
                }
            } catch (IOException ignored) {
            }
        }

        public int read(int pos, byte[] buf, int offset, int length) {
            if (!open)
                return -1;

            try {
                delay();

                file.seek(pos);
                return Math.max(0, file.read(buf, offset, length));
            } catch (IOException e) {
                return -1;
            }
        }

        public int write(int pos, byte[] buf, int offset, int length) {
            if (!open)
                return -1;

            try {
                delay();

                file.seek(pos);
                file.write(buf, offset, length);
                return length;
            } catch (IOException e) {
                return -1;
            }
        }

        public int length() {
            try {
                return (int) file.length();
            } catch (IOException e) {
                return -1;
            }
        }

        public void close() {
            if (open) {
                open = false;
                openCount--;
            }

            try {
                file.close();
            } catch (IOException ignored) {
            }
        }

        private RandomAccessFile file = null;
        private boolean open;
    }

    private int openCount = 0;
    private static final int maxOpenFiles = 16;

    private final Privilege privilege;
    private final File directory;

    private static boolean checkName(String name) {
        char[] chars = name.toCharArray();

        for (char aChar : chars) {
            if (aChar >= allowedFileNameCharacters.length)
                return false;
            if (!allowedFileNameCharacters[aChar])
                return false;
        }
        return true;
    }

    private static final boolean[] allowedFileNameCharacters = new boolean[0x80];

    private static void reject(char c) {
        allowedFileNameCharacters[c] = false;
    }

    private static void allow(char c) {
        allowedFileNameCharacters[c] = true;
    }

    private static void reject(char first, char last) {
        for (char c = first; c <= last; c++)
            allowedFileNameCharacters[c] = false;
    }

    private static void allow(char first, char last) {
        for (char c = first; c <= last; c++)
            allowedFileNameCharacters[c] = true;
    }

    static {
        reject((char) 0x00, (char) 0x7F);

        allow('A', 'Z');
        allow('a', 'z');
        allow('0', '9');

        allow('-');
        allow('_');
        allow('.');
        allow(',');
    }
}
